package com.csii.config.aspect;

import com.csii.adhoc.annotation.OperationLog;
import com.csii.adhoc.operationLog.OperationLogVo;
import com.csii.adhoc.operationLog.service.OperationLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.*;

/**
 * @Auther: panchuanjiang
 * @Date: 2021/01/12/9:30
 * @Description: 日志切面
 */
@Aspect
@Component
@Slf4j
@Order(2)
public class AsyncLogAspect {
    @Autowired
    private OperationLogService operationLogService;

    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(2,
            5, 60L, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.DiscardPolicy());

    private static final String LOCALIP = "0:0:0:0:0:0:0:1";

    //定义了切面的处理逻辑
    @Around("@annotation(operationLog)")
    public Object around(ProceedingJoinPoint joinPoint, OperationLog operationLog) throws Throwable {
        String userId = Optional.ofNullable(String.valueOf(SecurityUtils.getSubject().getPrincipal())).orElse("未知用户");
        String className = joinPoint.getTarget().getClass().getName();
        String logName = operationLog.value();
        MethodSignature ms = (MethodSignature) joinPoint.getSignature();
        String method = ms.getMethod().getName();
        Object[] paramsObj = joinPoint.getArgs();
        String params = null;
        //将参数转换为字符串
        try {
            params= new ObjectMapper().writeValueAsString(paramsObj);
        }catch (Exception e){
            log.error("获取参数异常",e);
        }
        String reqIp = getIpAddr();
        String optime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        OperationLogVo vo = new OperationLogVo();
        vo.setLogId(String.valueOf(System.currentTimeMillis()))
                .setLogSystem("ADHOC")
                .setUserId(userId)
                .setClassName(className)
                .setMethod(method)
                .setLogName(logName)
                .setAppIp(reqIp)
                .setMessage(params)
                .setOptime(optime);
        //捕捉了execute中抛出的异常
        Object result = null;
        try {
            //执行execute方法
            result = joinPoint.proceed();
            vo.setSucceed("succ");
        } catch (Exception e) {
            vo.setSucceed("fail");
            throw e; //不在切面处理异常
        } finally {
            executor.execute(() -> operationLogService.insertOne(vo));
        }
        return result;
    }

    public String getIpAddr() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("IPUtils ERROR ", e);
        }
        if (LOCALIP.equals(ip)) {
            ip = "127.0.0.1";
        }
        return ip;
    }

}
